package fd.sudoku.logical;

import java.util.ArrayList;
import java.util.Set;
import java.util.Map.Entry;

import fd.sudoku.data.SudokuMap;
import fd.sudoku.data.SudokuNumber;
import fd.sudoku.utility.SudokuCheck;

public class Processor {

	public static SudokuMap handler(SudokuMap map)
	{
		SudokuMap sudoku = map.clone();
		while(!SudokuCheck.checkMap(sudoku))
		{
			process(sudoku);
		}
		return sudoku;
	}

	//TODO
	private static void process(SudokuMap sudoku) 
	{
		//fake code
		//sudoku.set(9,9,8);
		implemetationProcess(sudoku);
	}
	
	private static void implemetationProcess(SudokuMap sudoku) 
	{
		checkBlock(sudoku);
		checkLine(sudoku);
		checkRow(sudoku);
		SudokuNumber sudokuNumber = getMinPossibleElement(sudoku);
		ArrayList<Integer> values = (ArrayList<Integer>) sudokuNumber.getPossibleValues().clone();
		
		for(Integer value : values)
		{
			sudoku.set(sudokuNumber.getMyLine(), sudokuNumber.getMyRow(), value);
			if(!SudokuCheck.checkMap(sudoku))
			{
				implemetationProcess(sudoku);
			}
		}
	}

	private static SudokuNumber getMinPossibleElement(SudokuMap sudoku)
	{
		SudokuNumber sudokuNumber = null;
		Set<Entry<Integer, SudokuNumber>> set = sudoku.getTheSudokuMap().entrySet();
		for(Entry<Integer, SudokuNumber> entry : set)
		{
			if(sudokuNumber == null)
			{
				sudokuNumber = entry.getValue();
			}
			else
			{
				if(entry.getValue().getValue()<=0 && entry.getValue().getPossibleValues().size()<sudokuNumber.getPossibleValues().size())
				{
					sudokuNumber = entry.getValue();
				}
			}
		}
		return sudokuNumber;
	}

	private static void checkRow(SudokuMap sudoku) 
	{
		for(int j = 1 ; j <= SudokuCheck.ROWSIZE ; j++)
		{
			ArrayList<SudokuNumber> emptyValues = new ArrayList<SudokuNumber>();
			ArrayList<Integer> values = new ArrayList<Integer>();
			for(int i = 1 ; i <= SudokuCheck.LINESIZE ; i++)
			{
				if(sudoku.get(i, j).getValue()<=0)
				{
					emptyValues.add(sudoku.get(i, j));
				}
				else
				{
					values.add(sudoku.get(i, j).getValue());
				}
				
			}
			ArrayList<Integer> possibleValues = SudokuCheck.getFullPossibleValues();
			possibleValues.removeAll(values);
			for(SudokuNumber emptyValue : emptyValues)
			{
				emptyValue.setPossibleValues(possibleValues);
			}
		}
	}

	private static void checkLine(SudokuMap sudoku)
	{
		for(int i = 1 ; i <= SudokuCheck.LINESIZE ; i++)
		{
			ArrayList<SudokuNumber> emptyValues = new ArrayList<SudokuNumber>();
			ArrayList<Integer> values = new ArrayList<Integer>();
			for(int j = 1 ; j <= SudokuCheck.ROWSIZE ; j++)
			{
				if(sudoku.get(i, j).getValue()<=0)
				{
					emptyValues.add(sudoku.get(i, j));
				}
				else
				{
					values.add(sudoku.get(i, j).getValue());
				}
				
			}
			ArrayList<Integer> possibleValues = SudokuCheck.getFullPossibleValues();
			possibleValues.removeAll(values);
			for(SudokuNumber emptyValue : emptyValues)
			{
				emptyValue.setPossibleValues(possibleValues);
			}
		}
	}

	private static void checkBlock(SudokuMap sudoku) 
	{
		for(int i = 0 ; i < SudokuCheck.BLOCKSIZE ; i++)
		{
			ArrayList<SudokuNumber> emptyValues = new ArrayList<SudokuNumber>();
			ArrayList<Integer> values = new ArrayList<Integer>();
			int startLine = (int) (((int)(i/3))*3+1);
			int startRow = (int) ((i%3)*3+1);
			for(int j = 0 ; j < SudokuCheck.SIZEOFBLOCK ; j++)
			{
				int addLine = (int) (j/3);
				int addRow = (int) (j%3);
				if(sudoku.get(startLine+addLine, addRow+startRow).getValue()<=0)
				{
					emptyValues.add(sudoku.get(startLine+addLine, addRow+startRow));
				}
				else
				{
					values.add(sudoku.get(startLine+addLine, addRow+startRow).getValue());
				}
				
			}
			ArrayList<Integer> possibleValues = SudokuCheck.getFullPossibleValues();
			possibleValues.removeAll(values);
			for(SudokuNumber emptyValue : emptyValues)
			{
				emptyValue.setPossibleValues(possibleValues);
			}
		}
	}
}
